﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cassandra;
using System.Threading.Tasks;

namespace mds.NoSqlDatabase
{
    public class CassandraProvider : ICassandraProvider
    {
        private static CassandraProvider _instance = new CassandraProvider();

        public static ICassandraProvider Instance
        {
            get { return _instance; }
        }
       
        private CassandraProvider()
        {
           
        }
        public bool AddkeySpace(string keySpace,string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect();
            var result = false;
            try
            {
                session.CreateKeyspace(keySpace);
                result = true;
            }
            catch(Exception ex)
            {
                NosqlLogService.Instance.Log("host:"+host+"增加cassandra空间:" + keySpace, ex);
                result = false;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
           
            return result;
        }

        public bool DeleteSpace(string keySpace,string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect(keySpace);
            var result = false;
            try
            {
                session.DeleteKeyspaceIfExists(keySpace);
                result = true;
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "删除cassandra空间:" + keySpace, ex);
                result = false;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
           
            return result;
        }

        public Guid AddFile(CassandraFile fileData, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect(fileData.KeySpace);
            PreparedStatement statement = session.Prepare("INSERT INTO " + fileData.KeySpace + ".files (id, filename,filesuffix,filesize,filedata,fileextension) VALUES (?, ?,?,?,?,?);");
            BoundStatement boundStatement = new BoundStatement(statement);
            Guid id = (fileData.Id == Guid.Empty || (fileData.Id == null)) ? Guid.NewGuid() : fileData.Id;
            var result = Guid.Empty;
            try
            {
                session.Execute(boundStatement.Bind(id, fileData.FileName, fileData.FileSuffix, fileData.FileSize, fileData.FileData, fileData.FileExtension));
                result = id;
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "AddFile失败", ex);
                
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
           
            return result;
        }
        /// <summary>
        ///适合同时存储多个空间下少量文件
        /// </summary>
        /// <param name="fileData"></param>
        /// <returns></returns>
        public Dictionary<Guid, bool> AddFiles(CassandraFile[] fileData, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            List<String> keySpaces = fileData.Select(a => a.KeySpace).Distinct().ToList();
            var list = fileData.ToList();
            Dictionary<Guid, bool> result = new Dictionary<Guid, bool>();
            String cql = "INSERT INTO $.files (id, filename,filesuffix,filesize,filedata,fileextension) VALUES (?, ?,?,?,?,?);";
            Parallel.ForEach(keySpaces, key =>
            {
                var session = cluster.Connect(key);
                PreparedStatement statement = session.Prepare(cql.Replace("$", key));
                BoundStatement boundStatement = new BoundStatement(statement);
                var files = list.FindAll(a => a.KeySpace == key);
                int count = files.Count();
                files.ForEach(dfsFile =>
                 {
                     bool sign = true;
                     Guid id = (dfsFile.Id == Guid.Empty || (dfsFile.Id == null)) ? Guid.NewGuid() : dfsFile.Id;
                     try
                     {
                         session.Execute(boundStatement.Bind(id, dfsFile.FileName, dfsFile.FileSuffix, dfsFile.FileSize, dfsFile.FileData, dfsFile.FileExtension));
                     }
                     catch (Exception ex)
                     {
                         NosqlLogService.Instance.Log("host:" + host + "AddFiles失败", ex);
                         sign = false;
                     }
                     result.Add(id, sign);
                 });
                session.Dispose();
            });
            cluster.Shutdown();
            cluster.Dispose();
            return result;
        }
        /// <summary>
        /// 适合单一空间批量文件存储
        /// </summary>
        /// <param name="fileData"></param>
        /// <param name="keySpace"></param>
        /// <returns></returns>
        public Dictionary<Guid, bool> AddBlockSingleSpace(CassandraFile[] fileData, String keySpace, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            Dictionary<Guid, bool> result = new Dictionary<Guid, bool>();
            String cql = "INSERT INTO " + keySpace + ".files (id, filename,filesuffix,filesize,filedata,fileextension) VALUES (?, ?,?,?,?,?);";
            Parallel.ForEach(fileData, dfsFile =>
            {
                var session = cluster.Connect(keySpace);
                PreparedStatement statement = session.Prepare(cql);
                BoundStatement boundStatement = new BoundStatement(statement);
                bool sign = true;
                Guid id = (dfsFile.Id == Guid.Empty || (dfsFile.Id == null)) ? Guid.NewGuid() : dfsFile.Id;
                try
                {
                    session.Execute(boundStatement.Bind(id, dfsFile.FileName, dfsFile.FileSuffix, dfsFile.FileSize, dfsFile.FileData, dfsFile.FileExtension));
                }
                catch (Exception ex)
                {
                    NosqlLogService.Instance.Log("host:" + host + "AddBlockSingleSpace失败", ex);
                    sign = false;
                }
                result.Add(id, sign);

                session.Dispose();
            });
            cluster.Shutdown();
            cluster.Dispose();
            return result;
        }
        public bool DropFile(Guid fileId, string keySpace, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect(keySpace);
            string cql = "Delete from " + keySpace + ".files where id=" + fileId.ToString();
            var result = false;
            try
            {
                session.Execute(cql);
                result = true;
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "DropFile失败", ex);
                result = false;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
           
            return result;

        }


        public CassandraFile GetFile(Guid fileId, string keySpace, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect(keySpace);
            string cql = "select * from " + keySpace + ".files where id=" + fileId.ToString();
            CassandraFile file = null;
            try
            {
                RowSet results = session.Execute(cql);
                foreach (Row row in results.GetRows())
                {
                    file = new CassandraFile()
                    {
                        FileData = row.GetValue<byte[]>("filedata"),
                        FileName = row.GetValue<string>("filename"),
                        FileSuffix = row.GetValue<string>("filesuffix"),
                        FileExtension = row.GetValue<string>("fileextension"),
                        KeySpace = keySpace,
                        Id = fileId,
                        FileSize = row.GetValue<int>("filesize")
                    };

                }
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "GetFile失败", ex);
                file = null;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
            return file;
        }


        public CassandraFileInfo GetFileInfo(Guid fileId, string keySpace, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect(keySpace);
            string cql = "select filename,filesuffix,filesize from " + keySpace + ".files where id=" + fileId.ToString();
            CassandraFileInfo file = null;
            try
            {
                RowSet results = session.Execute(cql);
                foreach (Row row in results.GetRows())
                {
                    file = new CassandraFileInfo()
                    {
                        FileName = row.GetValue<string>("filename"),
                        FileSuffix = row.GetValue<string>("filesuffix"),
                        KeySpace = keySpace,
                        Id = fileId,
                        FileSize = row.GetValue<int>("filesize")
                    };
                }
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "GetFileInfo失败", ex);
                file = null;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
            return file;
        }

        public CassandraFileInfo[] GetFileInfos(Guid[] fileId, string keySpace, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect(keySpace);
            string cql = "select * from " + keySpace + ".files where id=" + fileId.ToString();
            List<CassandraFileInfo> files = new List<CassandraFileInfo>();
            try
            {
                RowSet results = session.Execute(cql);
                foreach (Row row in results.GetRows())
                {
                    var file = new CassandraFileInfo()
                     {
                         FileName = row.GetValue<string>("filename"),
                         FileSuffix = row.GetValue<string>("filesuffix"),
                         KeySpace = keySpace,
                         Id = row.GetValue<Guid>("id"),
                         FileSize = row.GetValue<int>("filesize")
                     };
                    files.Add(file);
                }
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "GetFileInfos失败", ex);
                files = null;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
            return files.Count > 0 ? files.ToArray() : null;
        }
        public string[] GetSpaces(string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect();
            String cql = "SELECT * from system.schema_keyspaces;";
            List<string> spaces = new List<string>();
            try
            {
                session.Execute(cql);
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "GetSpaces失败", ex);
                spaces = null;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
            return spaces.Count > 0 ? spaces.ToArray() : null;
        }


        public bool CreateTable(string table, string keySpace, Dictionary<String, String> columns, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect(keySpace);
            String cql = "CREATE TABLE " + keySpace + "." + table + " (id uuid PRIMARY KEY,$);";
            StringBuilder strColumns = new StringBuilder();
            foreach (var kv in columns)
            {
                strColumns.Append(kv.Key + " " + kv.Value + ",");
            }
            cql = cql.Replace("$", strColumns.ToString().TrimEnd(','));
            var result = false;
            try
            {
                session.Execute(cql);
                result = true;
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "CreateTable失败", ex);
                result= false;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
            return result;
        }

        public bool DropTable(string table, string keySpace, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect(keySpace);
            String cql = "Drop TABLE " + keySpace + "." + table + ";";
            var result = false;
            try
            {
                session.Execute(cql);
                result = true;
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "DropTable失败", ex);
                result = false;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
            
            return result;
        }

        public string[] GetTables(string keySpace, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            ICollection<string> list = null;
            try
            {
                list = cluster.Metadata.GetTables(keySpace);
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "GetTables失败", ex);
                list = null;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();

            }
            return list.Count > 0 ? list.ToArray() : null;
        }



        public void AppendLog(string logContent, string applcationName, string ip,string host)
        {
           
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect("systemLog");
            PreparedStatement statement = session.Prepare("INSERT INTO systemLog.Log (Id,Message,ApplicationName,Ip,CreateTime) VALUES (?, ?,?,?,?,?);");
            BoundStatement boundStatement = new BoundStatement(statement);
            Guid id = Guid.NewGuid();
            try
            {
                session.Execute(boundStatement.Bind(id, logContent, applcationName, ip, DateTime.Now));
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "AppendLog失败", ex);
               
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }           
        
        }


        public CassandraErrorInfo[] GetLogs(String applcationName, string errorType, DateTime startTime,DateTime endTime, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect("systemLog");
            string cql = "select * from systemLog.errorLog where applcationname=" + applcationName + " and errortype=" + errorType + " and createtime>=" + startTime.ToString() + " and createtime<=" + endTime.ToString();
            List<CassandraErrorInfo> files = new List<CassandraErrorInfo>();
            try
            {
                RowSet results = session.Execute(cql);
                foreach (Row row in results.GetRows())
                {
                    var file = new CassandraErrorInfo()
                    {                           
                        ApplcationName = row.GetValue<string>("applcationname"),
                        CreateTime = row.GetValue<DateTime>("createtime"),
                        Id = row.GetValue<Guid>("id"),
                        ErrorType = row.GetValue<string>("errortype"),
                        LogContent = row.GetValue<string>("logcontent")
                    };
                    files.Add(file);
                }
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "GetLogs失败", ex);
                files = null;
            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }
            return files.Count > 0 ? files.ToArray() : null;
        }


        public void AppendErrorLog(string ApplicationName, DateTime CreateTime, string Host, string Id, string Ip, string Mac, string Message, DateTime ModifyTime, string Source, string SourceObject, int StatuCode, string Type, string host)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint(host).Build();
            var session = cluster.Connect("systemLog");
            PreparedStatement statement = session.Prepare("INSERT INTO systemLog.ErrorLog (ApplicationName, CreateTime,Host,Id,Ip,Mac,Message,ModifyTime,Source,SourceObject,StatuCode,Type) VALUES (?, ?,?,?,?,?,?, ?,?,?,?,?);");
            BoundStatement boundStatement = new BoundStatement(statement);
            Guid id = Guid.NewGuid();
            try
            {
                session.Execute(boundStatement.Bind(ApplicationName, DateTime.Now, Host, Id, Ip, Mac, Message, DateTime.Now, Source, SourceObject, StatuCode, Type));
            }
            catch (Exception ex)
            {
                NosqlLogService.Instance.Log("host:" + host + "AppendLog失败", ex);

            }
            finally
            {
                cluster.Shutdown();
                cluster.Dispose();
                session.Dispose();
            }          
        }
    }
}
